package com.orm.mybatis.utils;

import it.sauronsoftware.ftp4j.FTPClient;
import it.sauronsoftware.ftp4j.FTPException;
import it.sauronsoftware.ftp4j.FTPFile;
import it.sauronsoftware.ftp4j.FTPIllegalReplyException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.StringUtils;

import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;

/**
 * 刘伟强
 * 2021-10-20
 * 穿透TLS上传FTP
 */
@Slf4j
public class TLSFTPUtils {

   private static String remotePath = "/DebugLog";

   FTPClient client = new FTPClient();

   SSLContext sslContext = null;

   TrustManager[] trustManager = new TrustManager[] { new X509TrustManager() {

      public void checkClientTrusted(X509Certificate[] chain,
                                     String authType) throws CertificateException {
         // TODO Auto-generated method stub

      }

      public void checkServerTrusted(X509Certificate[] chain,
                                     String authType) throws CertificateException {
         // TODO Auto-generated method stub

      }

      public X509Certificate[] getAcceptedIssuers() {
         // TODO Auto-generated method stub
         return null;
      }

   } };


   public boolean connect(String hostname, String username, String password)  {
      try {
         sslContext = SSLContext.getInstance("SSL");
      } catch (NoSuchAlgorithmException e) {
         e.printStackTrace();
         log.error("SSL");
         return false;
      }
      try {
         sslContext.init(null, trustManager, new SecureRandom());
      } catch (KeyManagementException e) {
         e.printStackTrace();
         log.error("init");
         return false;
      }
      SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
      client.setSSLSocketFactory(sslSocketFactory);
      client.setSecurity(FTPClient.SECURITY_FTPES);
      client.setPassive(true);//设置被动
      client.setType(FTPClient.TYPE_BINARY);
      client.setCharset("gbk");
      try {
         client.connect(hostname);
      } catch (IOException | FTPIllegalReplyException | FTPException e) {
         e.printStackTrace();
         log.error("connect");
         return false;
      }
      log.info("连接"+hostname+"成功！"+client.toString());
      try {
         client.login(username, password);
      } catch (IOException | FTPIllegalReplyException | FTPException e) {
         e.printStackTrace();
         log.error("login");
         return false;
      }
      log.info("登录"+hostname+"成功！"+client.toString());

      return true;
   }

   /**
    * 上传文件或目录
    *
    * @param dir
    *            目标文件
    * @param del
    *            是否删除源文件，默认为false
    *            文件或目录对象数组
    * @throws Exception
    */
   public void upload(String dir,boolean del, File... files) throws Exception {
      if (StringUtils.isEmpty(dir) || files == null || files.length==0) {
         return;
      }
      try {
         mkdirs(dir); // 创建文件夹
         for (File file : files) {
            if (file.isDirectory()) { // 上传目录
               uploadFolder(getURL(dir), file, del);
            } else {
               client.changeDirectory(dir);
               String current2 = client.currentDirectory();
               System.out.println(current2);
               FTPFile[] files2 = client.list();
               FTPFile remoteFtpFile=null;
               boolean flag=false;
               for (FTPFile ftpFile:files2){
                  if (ftpFile.getName().equals(file.getName())){
                     flag=true;
                     remoteFtpFile=ftpFile;
                     break;
                  }
               }
               if (flag){
                  System.out.println("断点续传");
                  long remote_file_size=remoteFtpFile.getSize();
                  client.upload(file,remote_file_size);
                  log.debug("AbstractFtpEndpointComponent  upload  continue upload file success");
               }else {
                  System.out.println("从0上传");
                  client.upload(file);
               }
            }
         }
      } finally {
//         logout();
      }
   }
   /**
    * 上传文件或目录
    *
    * @param dir
    *            目标文件
    * @param files
    *            文件或目录对象数组
    * @throws Exception
    */
   public void upload(String dir, File... files) throws Exception {
      upload(dir, false, files);
   }
   /**
    * 上传文件或目录
    *
    * @param dir
    *            目标文件
    * @param del
    *            是否删除源文件，默认为false
    *            文件或目录路径数组
    * @throws Exception
    */
   public void upload(String dir, boolean del, String... paths)
           throws Exception {
      if (dir==null || paths == null || paths.length ==0) {
         return;
      }
      File[] files = new File[paths.length];
      for (int i = 0; i < paths.length; i++) {
         files[i] = new File(paths[i]);
      }
      upload(dir, del, files);
   }
   /**
    * 上传文件或目录
    *
    * @param dir
    *            远程目标文件
    * @param paths
    *            文件或目录路径数组
    * @throws Exception
    */
   public void upload(String dir, String... paths) throws Exception {
      upload(dir, false, paths);
   }
   /**
    * 上传目录
    *
    * @param parentUrl
    *            父节点URL
    * @param file
    *            目录
    * @throws Exception
    */
   private void uploadFolder( URL parentUrl, File file,
                             boolean del) throws Exception {
      client.changeDirectory(parentUrl.getPath());
      String dir = file.getName(); // 当前目录名称
      URL url = getURL(parentUrl, dir);
      if (!exists(url.getPath())) { // 判断当前目录是否存在
         client.createDirectory(dir); // 创建目录
      }
      client.changeDirectory(dir);
      File[] files = file.listFiles(); // 获取当前文件夹所有文件及目录
      for (int i = 0; i < files.length; i++) {
         file = files[i];
         if (file.isDirectory()) { // 如果是目录，则递归上传
            uploadFolder( url, file, del);
         } else { // 如果是文件，直接上传
//            client.changeDirectory(url.getPath());
            String current2 = client.currentDirectory();
            System.out.println(current2);
            FTPFile[] files2 = client.list();
            FTPFile remoteFtpFile=null;
            boolean flag=false;
            for (FTPFile ftpFile:files2){
               if (ftpFile.getName().equals(file.getName())){
                  flag=true;
                  remoteFtpFile=ftpFile;
                  break;
               }
            }
            if (flag){
               System.out.println("断点续传");
               long remote_file_size=remoteFtpFile.getSize();
               client.upload(file,remote_file_size);
               log.debug("AbstractFtpEndpointComponent  upload  continue upload file success");

            }else {
               System.out.println("从0上传");
               client.upload(file);
            }
            if (del){ // 删除源文件
               file.delete();
            }
         }
      }
   }
   /**
    * 删除文件或目录
    *
    *            文件或目录数组
    * @throws Exception
    */
   public void delete(String... dirs) throws Exception {
      if (dirs == null || dirs.length ==0) {
         return;
      }
      try {
         int type = -1;
         for (String dir : dirs) {
            client.changeDirectory("/"); // 切换至根目录
            type = getFileType(dir); // 获取当前类型
            if (type == 0) { // 删除文件
               client.deleteFile(dir);
            } else if (type == 1) { // 删除目录
               deleteFolder(client, getURL(dir));
            }
         }
      } finally {
//         logout();
      }
   }
   /**
    * 删除目录
    *
    * @param client
    *            FTP客户端对象
    * @param url
    *            FTP URL
    * @throws Exception
    */
   private void deleteFolder(FTPClient client, URL url) throws Exception {
      String path = url.getPath();
      client.changeDirectory(path);
      FTPFile[] files = client.list();
      String name = null;
      for (FTPFile file : files) {
         name = file.getName();
         // 排除隐藏目录
         if (".".equals(name) || "..".equals(name)) {
            continue;
         }
         if (file.getType() == FTPFile.TYPE_DIRECTORY) { // 递归删除子目录
            deleteFolder(client, getURL(url, file.getName()));
         } else if (file.getType() == FTPFile.TYPE_FILE) { // 删除文件
            client.deleteFile(file.getName());
         }
      }
      client.changeDirectoryUp();
      client.deleteDirectory(url.getPath()); // 删除当前目录
   }
   /**
    * 下载文件或目录
    *
    * @param localDir
    *            本地存储目录
    * @param dirs
    *            文件或者目录
    * @throws Exception
    */
   public void download(String localDir, String... dirs) throws Exception {
      if (dirs==null ||dirs.length ==0) {
         return;
      }
      try {
         File folder = new File(localDir);
         if (!folder.exists()) { // 如果本地文件夹不存在，则创建
            folder.mkdirs();
         }
         int type = -1;
         String localPath = null;
         for (String dir : dirs) {
            client.changeDirectory("/"); // 切换至根目录
            type = getFileType(dir); // 获取当前类型
            if (type == 0) { // 文件
               localPath = localDir + "/" + new File(dir).getName();
               client.download(dir, new File(localPath));
            } else if (type == 1) { // 目录
               downloadFolder(client, getURL(dir), localDir);
            }
         }
      } finally {
//         logout();
      }
   }
   /**
    * 下载文件夹
    *
    * @param client
    *            FTP客户端对象
    * @param url
    *            FTP URL
    * @param localDir
    *            本地存储目录
    * @throws Exception
    */
   private void downloadFolder(FTPClient client, URL url, String localDir)
           throws Exception {
      String path = url.getPath();
      client.changeDirectory(path);
      // 在本地创建当前下载的文件夹
      File folder = new File(localDir + "/" + new File(path).getName());
      if (!folder.exists()) {
         folder.mkdirs();
      }
      localDir = folder.getAbsolutePath();
      FTPFile[] files = client.list();
      String name = null;
      for (FTPFile file : files) {
         name = file.getName();
         // 排除隐藏目录
         if (".".equals(name) || "..".equals(name)) {
            continue;
         }
         if (file.getType() == FTPFile.TYPE_DIRECTORY) { // 递归下载子目录
            downloadFolder(client, getURL(url, file.getName()), localDir);
         } else if (file.getType() == FTPFile.TYPE_FILE) { // 下载文件
            client.download(name, new File(localDir + "/" + name));
         }
      }
      client.changeDirectoryUp();
   }

//   /**
//    * 获取目录下所有文件
//    *
//    * @param dir
//    *            远程目标文件
//    * @return
//    * @throws Exception
//    */
//   public String[] list(String dir) throws Exception {
////      FTPClient client = null;
//      try {
////         client = getClient();
//         client.changeDirectory(dir);
//         String[] values = client.listNames();
//         if (values != null) {
//            // 将文件排序(忽略大小写)
//            Arrays.sort(values, String::compareToIgnoreCase);
//         }
//         return values;
//      } catch(FTPException fe) {
//         // 忽略文件夹不存在的情况
//         String mark = "code=550";
//         if (!fe.toString().contains(mark)) {
//            throw fe;
//         }
//      } finally {
//         logout();
//      }
//      return new String[0];
//   }


   /**
    * 创建目录
    *            FTP客户端对象
    * @param dir
    *            目录
    * @throws Exception
    */
   private void mkdirs(String dir) throws Exception {
      if (dir == null || dir.equals("")) {
         return;
      }
      dir = FTPPathToolkit.formatPathFTP(dir);
      dir = dir.replace("//", "/");
      String[] dirs = dir.split("/");
      String path = "";
      for (int i = 0; i < dirs.length; i++) {
         path  +=  dirs[i] + "/";

         if (!isDirExist(path)) {
            client.createDirectory(path);// 创建目录
            client.changeDirectory(path);// 进入创建的目录
         }
      }
   }


   /**
    * 获取FTP目录
    *
    * @param url
    *            原FTP目录
    * @param dir
    *            目录
    * @return
    * @throws Exception
    */
   private URL getURL(URL url, String dir) throws Exception {
      String path = url.getPath();
      if (!path.endsWith("/") && !path.endsWith("//")) {
         path += "/";
      }
      dir = dir.replace("//", "/");
      if (dir.startsWith("/")) {
         dir = dir.substring(1);
      }
      path += dir;
      return new URL(url, path);
   }
   /**
    * 获取FTP目录
    *
    * @param dir
    *            目录
    * @return
    * @throws Exception
    */
   private URL getURL(String dir) throws Exception {
      if (dir.startsWith("/")) {
         dir = dir.substring(1);
      }
      return getURL(new URL("http://8.8.8.8"), dir);
   }

   /**
    * 判断文件或目录是否存在
    *
    *            FTP客户端对象
    * @param dir
    *            文件或目录
    * @return
    * @throws Exception
    */
   private boolean exists(String dir) throws Exception {
      return getFileType(dir) != -1;

   }

   //检查目录是否存在
   private boolean isDirExist(String dir) {
      try {
         client.changeDirectory(dir);
      } catch (Exception e) {
         return false;
      }
      return true;
   }


   /**
    * 判断当前为文件还是目录
    *
    *            FTP客户端对象
    * @param dir
    *            文件或目录
    * @return -1、文件或目录不存在 0、文件 1、目录
    */
   private int getFileType( String dir) {
      FTPFile[] files = null;
      try {
         files = client.list(dir);

      } catch (Exception e) {
         return -1;
      }
      if (files.length > 1) {
         return FTPFile.TYPE_DIRECTORY;
      } else if (files.length == 1) {
         FTPFile f = files[0];
         if (f.getType() == FTPFile.TYPE_DIRECTORY) {
            return FTPFile.TYPE_DIRECTORY;
         }
         String path = dir + "/" + f.getName();
         try {
            int len = client.list(path).length;
            if (len == 1) {
               return FTPFile.TYPE_DIRECTORY;
            } else {
               return FTPFile.TYPE_FILE;
            }
         } catch (Exception e) {
            return FTPFile.TYPE_FILE;
         }
      } else {
         try {
            client.changeDirectory(dir);
            client.changeDirectoryUp();
            return FTPFile.TYPE_DIRECTORY;
         } catch (Exception e) {
            return -1;
         }
      }
   }


   /**
    * 注销客户端连接
    *
    * @param
    *
    * @throws Exception
    */
   public void logout()  {
      if (client != null) {
         try {
            // 有些FTP服务器未实现此功能，若未实现则会出错
            client.logout(); // 退出登录
         } catch (Exception fe) {

         } finally {
            if (client.isConnected()) { // 断开连接
               try {
                  client.disconnect(true);
               } catch (IOException | FTPIllegalReplyException | FTPException e) {
                  e.printStackTrace();
                  System.out.println("退出异常");
               }
            }
         }
      }
   }


   /**
    * 断开与远程服务器的连接
    * @throws IOException
    *
    */
   public void disconnect() {
      if (client.isConnected()) {
         try {
            client.disconnect(true);
         } catch (IOException | FTPIllegalReplyException | FTPException e) {
            e.printStackTrace();
            System.out.println("退出异常");
         }
      }
   }

}
